// License Notification
//======================================================
// Copyright (c) 2010 Channel Intelligence Inc.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// ======================================================




using System;
using System.Collections.Generic;
using System.Data;
using System.Xml;
using System.Xml.Linq;
using CIP4.Common;

namespace CIP4.RulesEngine
{
    public class BooleanAtomType : AtomType, IBooleanValue
    {
        public override object AtomObjectValue
        {
            get { return AtomValue; }
        }

        public override CIP4PrimitiveType UnderlyingType
        {
            get { return CIP4PrimitiveType.CIP4Boolean; }
        }

        public bool? AtomValue { get; set; }

        public BooleanAtomType()
        {
        }

        public BooleanAtomType(bool value)
        {
            AtomValue = value;
            AtomSource = AtomSourceEnum.literal;
        }

        public BooleanAtomType(AtomSourceEnum atomSource, string atomName)
            : base(atomSource, atomName)
        {
        }

        public BooleanAtomType(XElement element)
            : base(element)
        {
            if (element.Name != XName.Get("BooleanAtom", RuleXmlNS))
                throw new ArgumentException(string.Format("{0} is not a valid xml for a boolean atom", element));
            if (!string.IsNullOrEmpty(element.Value))
            {
                bool val;
                if (!bool.TryParse(element.Value, out val))
                    throw new ArgumentException(string.Format("attempting to initialize a boolean atom with {0} failed because the value was not recognized as a boolean value", element));

                AtomValue = val;
            }
        }

        public override CIValueType Clone()
        {
            if (AtomSource == AtomSourceEnum.literal)
                return new BooleanAtomType(AtomValue ?? false); // if it's a literal it should have a value
            return new BooleanAtomType(AtomSource, AtomName);
        }

        public override object Evaluate(IDataPacketType data)
        {
            try
            {
                return Convert.ToBoolean(base.Evaluate(data));
            }
            catch (InvalidCastException ex)
            {
                data.Logger(MajorEnum.Warning, MinorEnum.ExpressionStructure, "unrecognized boolean value: {0}", new object[] { ex.Message});
                return false;
            }
        }

        public override void XmlFor(XmlWriter xwrtr, bool writeNamespace)
        {
            xwrtr.WriteStartElement("BooleanAtom");
            if (writeNamespace)
                xwrtr.WriteAttributeString("xmlns", RuleXmlNS);
            XmlFor(xwrtr);
            if (AtomValue.HasValue)
                xwrtr.WriteValue(AtomValue.ToString());
            xwrtr.WriteEndElement();
        }

        public bool Value(IDataPacketType data)
        {
            return Convert.ToBoolean(Evaluate(data));
        }

        public List<string> ComparesTo(string name)
        {
            return new List<string>();
        }

        public override string ToString(ExpressionFormatEnum format)
        {
            switch (format)
            {
                case ExpressionFormatEnum.javascript:
                    return ToJavaScriptString();
                default:
                    return ToString();
            }
        }

        public override string ToString()
        {
            if (AtomSource == AtomSourceEnum.literal)
            {
                object value = base.Evaluate(new RulesEngineDataPacket());
                if (value != null)
                    return (Convert.ToBoolean(value)) ? "true" : "false";

                return "nothing";
            }
            if (AtomSource == AtomSourceEnum.namedValue)
                return AtomName;
            if (AtomSource == AtomSourceEnum.parameter)
                return "Parameter." + AtomName;
            if (AtomSource == AtomSourceEnum.function)
                return "Call " + AtomName + "()";
            if (AtomSource == AtomSourceEnum.variable)
                return "Call XPath( " + AtomName + ")";
            return "InvalodOp";
        }

        private string ToJavaScriptString()
        {
            switch (AtomSource)
            {
                case AtomSourceEnum.literal:
                    object value = base.Evaluate(new RulesEngineDataPacket());
                    if (value != null)
                        return (Convert.ToBoolean(value)) ? "true" : "false";

                    throw new InvalidExpressionException("Cannot evaluate boolean literal.");
                
                case AtomSourceEnum.parameter:
                    if (string.IsNullOrEmpty(AtomName))
                        throw new InvalidExpressionException("Blank atom name for boolean atom.");
                
                    return "parameter['" + AtomName.ToUpper() + "']";

                default:
                    throw new InvalidExpressionException("Unhandled boolean atom source: " + AtomSource);
            }
        }
    }
}
